"use strict";

Components.utils.import("resource://gre/modules/XPCOMUtils.jsm")
    //mponents.utils.import("resource://gre/modules/Services.jsm")
    //mponents.utils.import("resource://gre/modules/PlacesUtils.jsm")

Components.utils.import("resource://iplusnewtab/modules/utility.jsm")

if ("undefined" == typeof(ip2newtab)) {
    var ip2newtab = {    }
}

/*  Storage: [  ip2newtab
    { host : first day }
    { host : { addr {}, addr {}, ... } }
]   */

//  in:     [ ip-address { from, last } ]
//  target: find latest of ip-address
//  out:    { ip-address, found-timestamp }
ip2newtab.getAddress = function(addr2pair)
    {
        var thaddr = null;
        var garbage = null;
        var thedate = Date.parse(new Date(2000, 2, 2));
        for (let index in addr2pair)
        {
            let thefrom = addr2pair[index].from;
                thefrom = Date.parse(thefrom);
            if(thedate < thefrom)
            {
                thedate = thefrom;
                thaddr = index;
            }
            else
            if(!thefrom) garbage = index;
        }

        if(thaddr) {
            if(garbage) delete addr2pair[garbage];
            return { addr: thaddr, stamp: thedate };
        }

        return null;
    }

//  in:
//      ip-address { from, last }
//      registration date
//      delta from options page, hour to fraction
//      ip-address and timestamp from getAddress
//  target:
//      if timestamp plus delta over now
//      if host has past of other ip-address
//  out: alarm flag
ip2newtab.i314age = function(acache, astart, adelta, apair)
    {
        var retval = false;
        var thenow = Date.now();
            astart = Date.parse(astart);
        if((apair.stamp + adelta) > thenow)
        if((astart + adelta) < thenow)
        try {
            for (let index in acache)
            {
                let theval = Date.parse(acache[index].from);
                if((!theval) || ((theval + adelta) < thenow))
                {
                    retval = true;
                    break;
                }
            }
        }
        catch(err)  {
            Components.utils.reportError(err)
        }
        return retval;
    }

//  in: [ { "addr": addr, count: 0, from: , last: } ]
//  target: cache update storage
// out (this) : [ ip-address { from, last } ]
ip2newtab.cache2storage = function(avalue)  // addr2pair)
    {
        let thaddr = avalue.addr;
        if(thaddr in this)
        {
            if(!(this[thaddr].last > avalue.last))
                this[thaddr].last = avalue.last;
        }
        else
            this[thaddr] = { from: avalue.from, last: avalue.last };
    }
// ip2newtab.cache2storage(thecache, thedisk);

ip2newtab.updateDraggable = function(alist)
    {
    //  set restoring of default behaviour
        for each (let thumb in alist)
            if(thumb.href)
        {
            thumb.addEventListener("mouseenter",
                function(evt)
                {
                    if(evt.currentTarget.parentNode)
                    evt.currentTarget.parentNode.setAttribute("draggable", "true");
                }, false);
        }

        var thelist = document.querySelectorAll("span.newtab-ip2header");
    //  window.console.info("span.newtab-ip2header: ", thelist.length);
        for (let i = 0; i < thelist.length; ++i)
        {
            let thitem = thelist[i];

            if(thitem.textContent)
            thitem.addEventListener("mouseleave",
                function(evt)
                {
                    if(evt.currentTarget.parentNode)
                    evt.currentTarget.parentNode.setAttribute("draggable", "true");
                },
                false);

            if(thitem.textContent)
            thitem.addEventListener("mouseenter",
                function(evt) {
                    evt.currentTarget.parentNode.setAttribute("draggable", "false");
                }, false);
        }
    }

   //mponents.utils.import("resource:///modules/NewTabUtils.jsm")
ip2newtab.Storage = { //  Singleton that provides storage functionality.
  /**
   * The dom storage instance used to persist data belonging to the New Tab Page.
   */
  get domStorage()
  {
    let uri = Services.io.newURI("about:newtab", null, null);
    let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
    let sm = Services.domStorageManager;
    let storage = sm.getLocalStorageForPrincipal(principal, "");

    // Cache this value, overwrite the getter.
    let descriptor = { value: storage, enumerable: true };
    Object.defineProperty(this, "domStorage", descriptor);

    return storage;
  },

  /**
   * Gets the value for a given key from the storage.
   * @param aKey The storage key (a string).
   * @param aDefault A default value if the key doesn't exist.
   * @return The value for the given key.
   */
  get: function Storage_get(aKey, aDefault) {
    let value;

    try {
      value = JSON.parse(this.domStorage.getItem(aKey));
    } catch (e) {}

    return value || aDefault;
  },

  /**
   * Sets the storage value for a given key.
   * @param aKey The storage key (a string).
   * @param aValue The value to set.
   */
  set: function Storage_set(aKey, aValue) {
    this.domStorage.setItem(aKey, JSON.stringify(aValue));
  },

  /**
   * Clears the storage and removes all values.
   */
  clear: function Storage_clear() {
    this.domStorage.clear();
  },

  QueryInterface: XPCOMUtils.generateQI([
                    Components.interfaces.nsISupports,
    //                  Components.interfaces.nsIObserver,
                Components.interfaces.nsISupportsWeakReference  ])
}

ip2newtab.main = {
    cacheHost : [],    // host cache for random clearing

    //  query(a.newtab-link).href
    //  update storage from cache
    //  set ip(host) at top of thumbnail
  startup: function()
  {
    var register = { };   //  double object
    var host2addr= { };   //  are saved in Storage
    var thestor  = ip2newtab.Storage.get("ip2newtab", []);
    var thelen = 0;
    if(thestor.length === 2)
    try {
        let [theone, thenext] = thestor;
        thelen = Object.keys(thenext).length;
        if(thelen) [ register, host2addr ] = [theone, thenext];
    }
    catch(err)  {
        thelen = 0;
        Components.utils.reportError(err)
    }

    if(!thelen) thestor = [ register, host2addr ];

    //  select thumbnail from a where newtab-link
    var thelist = document.querySelectorAll("a.newtab-link");
    var thour = iplusnewtab.getDelta();
    var thesuspect = false; // needs synchronization register & host2addr

    function procForEach(athumb)
        {
            var thehost = (athumb.hostname || "").toLowerCase();
            if(!(thehost.length >> 1)) return false;
            var retval = false;

        //  form header
            let thefoot = athumb.querySelector("span.newtab-title");
            let newnode = thefoot.cloneNode(false);
            newnode.setAttribute("class", "newtab-ip2header");

        //  update storage
            var thecache = iplusnewtab.getStreet(thehost);
            var thedisk = null;

            if(thehost in host2addr)
            {
                thedisk = host2addr[thehost];
                let garbage = null;
                for (let iddress in thedisk)
                {
                    let theval = thedisk[iddress].last;
                    if(Date.parse(theval))
                        thedisk[iddress].last = new Date(theval)
                    else garbage = iddress;
                }

                if(garbage) delete thedisk[garbage];
            }

            if(thecache.length)
            {
                if(!thedisk) host2addr[thehost] = thedisk = { };
                //  copy street 2 storage ip2newtab.cache2storage(thecache, thedisk);
                thecache.forEach(ip2newtab.cache2storage, thedisk)
            }

            if(thedisk)
            try {
                ip2newtab.main.cacheHost.push(thehost);

                let thaddr = ip2newtab.getAddress(thedisk);
                if(thaddr)
                {
//        window.console.info(thehost, ": ", register[thehost]);  // _dvk_dbg_    
                    if(Date.parse(register[thehost]))
                        register[thehost] = new Date(register[thehost])
                    else
                        register[thehost] = new Date(thaddr.stamp);
    
            //  choice of style
                    let thewarn = ip2newtab.i314age(thedisk, register[thehost], thour, thaddr);
        //        dump("title:\t"); .style.color ? or background-color: rgba(248,249,0,1);
    //                  if(thewarn) newnode.style.backgroundColor = "rgb(248,249,0)";
                    if(thewarn) newnode.setAttribute("value", "mark");
            //  set front values
                    newnode.textContent = thaddr.addr;
                    newnode.title = (new Date(thaddr.stamp)).toLocaleDateString();
                }
                else retval = true;
            }
            catch(err) {    //  e.g: local storage is not consistency
                retval = true;
            }
            
            athumb.parentNode.insertBefore(newnode, athumb.nextElementSibling);    
    //  athumb.addEventListener("mouseenter", ip2newtab.setDraggable, false);

            return retval;
        }   //  procForEach

    for (let i = 0; i < thelist.length; ++i)
        if(thelist[i].href)
            if(procForEach(thelist[i]))
                thesuspect = true;

        thelen = Object.keys(host2addr).length;
    let thenext = Object.keys(register).length;
    if((!thesuspect) && (thelen === thenext))
    {
        if(thenext) ip2newtab.Storage.set("ip2newtab", thestor)
    }
    else    //  write or synchronize
        window.setTimeout( function(aregister, ahost2addr)
            { ip2newtab.synchronize(aregister, ahost2addr) },
                    1, register, host2addr );

    //  part of usual selecting sub system: reseting draggable attribute        
    if(this.cacheHost.length)
        window.setTimeout(function(alist)
            { ip2newtab.updateDraggable(alist) }, 0, thelist);

    if(!(thelen >> 5))   //  in re random clearing
        window.removeEventListener("pagehide", ip2newtab.main, false);
  },

  //    and sanitation
  synchronize : function(aregister, ahost2addr)
  {
    //  cut register if not found in host2addr
    for (let thehost in aregister)
        if(!(thehost in ahost2addr))
            delete aregister[thehost];

    var thegarbage = [];
    //  move from host2addr to register or check from < last
    for (let thehost in ahost2addr)
    {
        let thedisk = ahost2addr[thehost];
        if(Object.keys(thedisk).length)
        {
            let corrupt = null;
            let thefrom = new Date();
            for (let index in thedisk)
            {
                thedisk[index].from = new Date(thedisk[index].from);
                thedisk[index].last = new Date(thedisk[index].last);
                if(thedisk[index].from < thedisk[index].last)
                {
                    if(thefrom > thedisk[index].from)
                        thefrom = thedisk[index].from
                    else
                        if(!(Date.parse(thedisk[index].from)))
                            corrupt = index;
                }
                else
                    if(Date.parse(thedisk[index].from)
                        == Date.parse(thedisk[index].last))
                            continue
                    else corrupt = index;
            }

            if(corrupt) delete thedisk[corrupt];
            if(!(aregister[thehost])) aregister[thehost] = thefrom;
        }
        else
            thegarbage.push(thehost);

    }

//    window.console.info("_dvk_dbg_, synchronize: ", thegarbage.length);
//    thegarbage.forEach( function(index) { window.console.info(index) } );
    thegarbage.forEach( function(index) { delete ahost2addr[index] } );

    ip2newtab.Storage.set("ip2newtab", [aregister, ahost2addr]);
  },

  shutdown : function()
  {
    let register = null;
    var thekeys = [];
    var thestor  = ip2newtab.Storage.get("ip2newtab", []);
    if(thestor.length === 2)
    try {
        let [ theone, thenext ] = thestor;
            thekeys = Object.keys(theone);
        if(thekeys) [ register, thenext ] = [ theone, thenext ];
    }
    catch(err)  {
        Components.utils.reportError(err)
    }

    var thelen = thekeys.length;
    if(!thelen || !register) return;

    var thehost = thekeys[ Date.now() % thelen ];
    if(this.cacheHost.indexOf(thehost) + 1) return;
        
    let options = PlacesUtils.history.getNewQueryOptions();
        options.maxResults = 2;
    
let callback = {    //  places.sqlite   (url, title, visit_count, ...., last_visit_date)
        handleResult: function (aResultSet)
        {
            if(aResultSet) thehost = null;
        },

        handleError: function (aError)
        {
            thehost = null;
        },

        handleCompletion: function (aReason)
        {
            if(thehost)
            {
                let [ register, host2addr ] = thestor;
                delete host2addr[thehost];
                delete register[thehost];
                ip2newtab.Storage.set("ip2newtab", thestor);
            }
        }
};

        let query = PlacesUtils.history.getNewQuery();
            query.domain = thehost;
            query.domainIsHost = false;

        let db = PlacesUtils.history.QueryInterface(Components.interfaces.nsPIPlacesDatabase);
            db.asyncExecuteLegacyQueries([query], 1, options, callback);

    return;
  },

  handleEvent : function(evt)
  {
    window.removeEventListener(evt.type, this, false);
    if (evt.type == "load") this.startup()
        else this.shutdown();
  }
}
